package org.dreamwork.jasmine2.engine;

import org.dreamwork.util.VirtualFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletInputStream;
import javax.servlet.RequestDispatcher;
import java.util.*;
import java.security.Principal;
import java.io.*;

/**
 * HttpServletRequest 包装器.
 * 
 * <p>包装 <code>HttpServletRequest</code> 接口，使其内置支持文件上传。
 */
public class JasmineRequest implements HttpServletRequest {
    private HttpServletRequest request;
    private Hashtable<String, Object> map = new Hashtable<String, Object> ();
    private Map<String, VirtualFile> files = new HashMap<String, VirtualFile> ();

    private String boundary = null;

    public JasmineRequest (HttpServletRequest request) throws IOException {
        this.request = request;
        parse ();
    }

    public HttpServletRequest getOrigRequest () {
        return request;
    }

    private void parse () throws IOException {
        String contentType = request.getHeader ("content-type");
        int pos = contentType.lastIndexOf ("boundary=");
        if (pos != -1) boundary = contentType.substring (pos + "boundary=".length ());

        if (boundary != null) {
            ServletInputStream in = request.getInputStream ();
            byte[] buff = new byte[1024];
            int len;
            String fieldName = null, fileName = null, type = null;
//            ByteArrayOutputStream baos = new ByteArrayOutputStream ();
            OutputStream out = null;
            boolean start = false;
            String enc = request.getCharacterEncoding ();
            VirtualFile vfile = null;
            while ((len = in.readLine (buff, 0, buff.length)) != -1) {
                String line = enc == null ? new String (buff, 0, len) : new String (buff, 0, len, enc);
                if (line.startsWith ("Content-Disposition: form-data;")) { // field start
                    String[] a = line.split (";");
                    for (int i = 1; i < a.length; i++) {
                        String[] t = a [i].split ("=");
                        if (t[0].trim ().equals ("name")) {
                            fieldName = t[1].trim ();
                            fieldName = fieldName.substring (1, fieldName.length () - 1);
                        }
                        if (t[0].trim ().equals ("filename")) {
                            fileName = t[1].trim ();
                            fileName = fileName.substring (1, fileName.length () - 1);
                            len = in.readLine (buff, 0, buff.length);
                            line = enc == null ? new String (buff, 0, len) : new String (buff, 0, len, enc);
                            type = line.substring ("Content-Type: ".length ()).trim ();
                        }
                    }
                    in.readLine (buff, 0, buff.length);
                } else if (line.startsWith ("--" + boundary)) {
                    if (!start) continue;
                    if (fileName == null) {
                        ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
                        String value = enc == null ?
                            new String (baos.toByteArray ()).trim () :
                            new String (baos.toByteArray (), enc).trim ();
                        map.put (fieldName, value);
                    } else {
//                        VirtualFile file = new VirtualFile ();
                        vfile.setClientPath (getPath (fileName));
                        vfile.setName (getName (fileName));
                        vfile.setType (type);
                        out.flush ();
                        out.close ();
//                        file.setContent (baos.toByteArray ());
                        files.put (fieldName, vfile);
                    }
                    fieldName = null;
                    fileName = null;
                    type = null;
                    out = null;
                } else {
                    if (out == null) {
                        if (fileName == null) out = new ByteArrayOutputStream ();
                        else {
                            vfile = new VirtualFile (true);
                            out = vfile.getOutputStream ();
                        }
                    }
                    out.write (buff, 0, len);
                    start = true;
                }
            }
        }
    }

    private String getPath (String fileName) {
        String t = fileName.replace ('\\', '/');
        int pos = t.lastIndexOf ('/');
        if (pos != -1) return fileName.substring (0, pos);

        return "";
    }

    private String getName (String fileName) {
        String t = fileName.replace ('\\', '/');
        int pos = t.lastIndexOf ('/');
        if (pos != -1) return fileName.substring (pos + 1);
        return fileName;
    }

    public String getEncoding () {
        String encHeader = request.getHeader ("accept-charset");
        int pos = encHeader.indexOf (",");
        if (pos >= 0) return encHeader.substring (0, pos);
        return encHeader;
    }

    public String getAuthType () {
        return request.getAuthType ();
    }

    public Cookie[] getCookies () {
        return request.getCookies ();
    }

    public long getDateHeader (String name) {
        return request.getDateHeader (name);
    }

    public String getHeader (String name) {
        return request.getHeader (name);
    }

    public Enumeration getHeaders (String name) {
        return request.getHeaders (name);
    }

    public Enumeration getHeaderNames () {
        return request.getHeaderNames ();
    }

    public int getIntHeader (String name) {
        return request.getIntHeader (name);
    }

    public String getMethod () {
        return request.getMethod ();
    }

    public String getPathInfo () {
        return request.getPathInfo ();
    }

    public String getPathTranslated () {
        return request.getPathTranslated ();
    }

    public String getContextPath () {
        return request.getContextPath ();
    }

    public String getQueryString () {
        return request.getQueryString ();
    }

    public String getRemoteUser () {
        return request.getRemoteUser ();
    }

    public boolean isUserInRole (String name) {
        return request.isUserInRole (name);
    }

    public Principal getUserPrincipal () {
        return request.getUserPrincipal ();
    }

    public String getRequestedSessionId () {
        return request.getRequestedSessionId ();
    }

    public String getRequestURI () {
        return request.getRequestURI ();
    }

    public StringBuffer getRequestURL () {
        return request.getRequestURL ();
    }

    public String getServletPath () {
        return request.getServletPath ();
    }

    public HttpSession getSession (boolean b) {
        return request.getSession (b);
    }

    public HttpSession getSession () {
        return request.getSession ();
    }

    public boolean isRequestedSessionIdValid () {
        return request.isRequestedSessionIdValid ();
    }

    public boolean isRequestedSessionIdFromCookie () {
        return request.isRequestedSessionIdFromCookie ();
    }

    public boolean isRequestedSessionIdFromURL () {
        return request.isRequestedSessionIdFromURL ();
    }

    /**
     * @deprecated
     * @return boolean
     */
    @SuppressWarnings ({"unchecked", "deprecated"})
    public boolean isRequestedSessionIdFromUrl () {
        return request.isRequestedSessionIdFromUrl ();
    }

    public Object getAttribute (String name) {
        return request.getAttribute (name);
    }

    public Enumeration getAttributeNames () {
        return request.getAttributeNames ();
    }

    public String getCharacterEncoding () {
        return request.getCharacterEncoding ();
    }

    public void setCharacterEncoding (String enc) throws UnsupportedEncodingException {
        request.setCharacterEncoding (enc);
    }

    public int getContentLength () {
        return request.getContentLength ();
    }

    public String getContentType () {
        return request.getContentType ();
    }

    public ServletInputStream getInputStream () throws IOException {
        return request.getInputStream ();
    }

    public String getParameter (String name) {
        return (String) map.get (name);
    }

    public Enumeration getParameterNames () {
        return map.keys ();
    }

    @SuppressWarnings ("unchecked")
    public String[] getParameterValues (String name) {
        Object o = map.get (name);
        if (o instanceof List) {
            List list = (List) o;
            String[] values = new String [list.size ()];
            return (String[]) list.toArray (values);
        } else if (o instanceof String) {
            return new String[] {(String) o};
        }
        return null;
    }

    public Map getParameterMap () {
        return map;
    }

    public String getProtocol () {
        return request.getProtocol ();
    }

    public String getScheme () {
        return request.getScheme ();
    }

    public String getServerName () {
        return request.getServerName ();
    }

    public int getServerPort () {
        return request.getServerPort ();
    }

    public BufferedReader getReader () throws IOException {
        return request.getReader ();
    }

    public String getRemoteAddr () {
        return request.getRemoteAddr ();
    }

    public String getRemoteHost () {
        return request.getRemoteHost ();
    }

    public void setAttribute (String name, Object value) {
        request.setAttribute (name, value);
    }

    public void removeAttribute (String name) {
        request.removeAttribute (name);
    }

    public Locale getLocale () {
        return request.getLocale ();
    }

    public Enumeration getLocales () {
        return request.getLocales ();
    }

    public boolean isSecure () {
        return request.isSecure ();
    }

    public RequestDispatcher getRequestDispatcher (String path) {
        return request.getRequestDispatcher (path);
    }

    /**
     * @deprecated
     * @param path path
     * @return string
     */
    public String getRealPath (String path) {
        return request.getRealPath (path);
    }

    public int getRemotePort () {
        return request.getRemotePort ();
    }

    public String getLocalName () {
        return request.getLocalName ();
    }

    public String getLocalAddr () {
        return request.getLocalAddr ();
    }

    public int getLocalPort () {
        return request.getLocalPort ();
    }

    public Iterator getVirtualFileNames () {
        return files.keySet ().iterator ();
    }

    public VirtualFile[] getVirtualFiles () {
        VirtualFile[] files = new VirtualFile[this.files.size ()];
        return this.files.values ().toArray (files);
    }

    public VirtualFile getVirtualFile (String name) {
        return files.get (name);
    }

    public int getVirtualFileCount () {
        return files.size ();
    }
}
